use crate::core::error::convert_error::StrToPathBufSnafu;
use crate::core::error::operation_error::OperationError;
pub use crate::core::error::ParrotError;
use crate::core::operation::Operation;
use crate::core::step_context::StepContext;
use snafu::{ResultExt, Snafu};
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use tonic::async_trait;
use walkdir::WalkDir;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum Error {
    #[snafu(display("An io error occurred: {source}"))]
    IO { source: std::io::Error },
}

impl From<Error> for OperationError {
    fn from(value: Error) -> Self {
        OperationError::CopyEntriesInto { source: value }
    }
}

pub struct CopyEntriesInto {
    src: PathBuf,
    dst: PathBuf,
}

impl CopyEntriesInto {
    pub fn new(src: &str, dst: &str) -> Result<Self, ParrotError> {
        Ok(CopyEntriesInto {
            src: PathBuf::from_str(&src).context(StrToPathBufSnafu {
                path: &src.to_string(),
            })?,
            dst: PathBuf::from_str(&dst).context(StrToPathBufSnafu {
                path: &dst.to_string(),
            })?,
        })
    }
}

#[async_trait]
impl Operation for CopyEntriesInto {
    async fn run(&mut self, context: Arc<StepContext>) -> Result<(), OperationError> {
        fs::create_dir_all(&self.dst).context(IOSnafu {})?;

        for e in WalkDir::new(&self.src)
            .into_iter()
            .filter_map(Result::ok)
            .filter(|e| !e.file_type().is_file())
        {
            fs::copy(e.path(), &self.dst.join(e.file_name())).context(IOSnafu {})?;
        }

        Ok(())
    }
}
